/*
 * Copyright 2020-2030 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gitee.minimalismstyle.fresh.common.web.config

import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer
import com.gitee.minimalismstyle.fresh.common.core.const.DefaultConst
import com.gitee.minimalismstyle.fresh.common.web.deserializer.NumberToLocalDateDeserializer
import com.gitee.minimalismstyle.fresh.common.web.deserializer.NumberToLocalDateTimeDeserializer
import com.gitee.minimalismstyle.fresh.common.web.resolver.PageParamsResolver
import com.gitee.minimalismstyle.fresh.common.web.serializer.LocalDateTimeToNumberSerializer
import com.gitee.minimalismstyle.fresh.common.web.serializer.LocalDateToNumberSerializer
import org.springframework.beans.BeansException
import org.springframework.beans.factory.config.BeanPostProcessor
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.HttpMessageConverter
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
import org.springframework.util.ReflectionUtils
import org.springframework.web.method.support.HandlerMethodArgumentResolver
import org.springframework.web.servlet.config.annotation.EnableWebMvc
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider
import java.lang.reflect.Field
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import java.util.stream.Collectors


/**
 *  web统一配置
 * @author maoxiaodong
 */
@EnableWebMvc
@Configuration
class WebMvcConfig(val pageParamsResolver: PageParamsResolver) : WebMvcConfigurer {

    override fun configureMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
        //将对象中的long类型转换成string
        val jackson2HttpMessageConverter = MappingJackson2HttpMessageConverter()
        val objectMapper = ObjectMapper()
        val simpleModule = JavaTimeModule()
        simpleModule.addSerializer(java.lang.Long::class.java, ToStringSerializer.instance)
        simpleModule.addSerializer(Long::class.java, ToStringSerializer.instance)

        simpleModule.addSerializer(LocalDateTime::class.java, LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DefaultConst.DEFAULT_DATE_TIME_PATTERN)))
        simpleModule.addSerializer(LocalDate::class.java, LocalDateSerializer(DateTimeFormatter.ofPattern(DefaultConst.DEFAULT_DATE_PATTERN)))
        simpleModule.addSerializer(LocalTime::class.java, LocalTimeSerializer(DateTimeFormatter.ofPattern(DefaultConst.DEFAULT_TIME_PATTERN)))
        simpleModule.addDeserializer(LocalDateTime::class.java, LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DefaultConst.DEFAULT_DATE_TIME_PATTERN)))
        simpleModule.addDeserializer(LocalDate::class.java, LocalDateDeserializer(DateTimeFormatter.ofPattern(DefaultConst.DEFAULT_DATE_PATTERN)))
        simpleModule.addDeserializer(LocalTime::class.java, LocalTimeDeserializer(DateTimeFormatter.ofPattern(DefaultConst.DEFAULT_TIME_PATTERN)))
        simpleModule.addSerializer(LocalDateTime::class.java, LocalDateTimeToNumberSerializer())
        simpleModule.addSerializer(LocalDate::class.java, LocalDateToNumberSerializer())
        simpleModule.addDeserializer(LocalDateTime::class.java, NumberToLocalDateTimeDeserializer())
        simpleModule.addDeserializer(LocalDate::class.java, NumberToLocalDateDeserializer())
        //objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(simpleModule)
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        jackson2HttpMessageConverter.objectMapper = objectMapper
        converters.add(jackson2HttpMessageConverter)
    }


    override fun addArgumentResolvers(resolvers: MutableList<HandlerMethodArgumentResolver>) {
        //解析通用查询参数
        resolvers.add(pageParamsResolver)
    }


    @Bean
    fun springfoxHandlerProviderBeanPostProcessor(): BeanPostProcessor? {
        return object : BeanPostProcessor {
            @Throws(BeansException::class)
            override fun postProcessAfterInitialization(bean: Any, beanName: String): Any? {
                if (bean is WebMvcRequestHandlerProvider || bean is WebFluxRequestHandlerProvider) {
                    customizeSpringfoxHandlerMappings<RequestMappingInfoHandlerMapping>(getHandlerMappings(bean))
                }
                return bean
            }

            private fun <T : RequestMappingInfoHandlerMapping?> customizeSpringfoxHandlerMappings(mappings: MutableList<T>) {
                val copy = mappings.stream()
                        .filter { mapping: T -> mapping?.patternParser == null }
                        .collect(Collectors.toList())
                mappings.clear()
                mappings.addAll(copy)
            }

            private fun getHandlerMappings(bean: Any): MutableList<RequestMappingInfoHandlerMapping> {
                val field: Field = ReflectionUtils.findField(bean.javaClass, "handlerMappings")!!
                field.isAccessible = true
                return field.get(bean) as MutableList<RequestMappingInfoHandlerMapping>
            }
        }
    }


}